package lc.com.lcpicturebackend.manager.websocket;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import jakarta.annotation.Resource;
import lc.com.lcpicturebackend.manager.websocket.disruptor.PictureEditEventProducer;
import lc.com.lcpicturebackend.manager.websocket.model.PictureEditActionEnum;
import lc.com.lcpicturebackend.manager.websocket.model.PictureEditMessageTypeEnum;
import lc.com.lcpicturebackend.manager.websocket.model.PictureEditRequestMessage;
import lc.com.lcpicturebackend.manager.websocket.model.PictureEditResponseMessage;
import lc.com.lcpicturebackend.model.entity.User;
import lc.com.lcpicturebackend.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 功能：webSocket 处理器
 * 作者：lc
 * 日期：2025/4/23 23:23
 */
@Component
@Slf4j
public class PictureEditHandler extends TextWebSocketHandler {

    @Resource
    private UserService userService;

    @Resource
    private PictureEditEventProducer pictureEditEventProducer;
    /**
     * 每张图片的会话类表，key :pictureId, value: 用户会话集合
     */
    private final Map<Long, Set<WebSocketSession>> pictureSession = new ConcurrentHashMap<>();
    /**
     * 每张图片的编辑状态，key :pictureId, value: 当前编辑的用户id
     */
    private final Map<Long, Long> pictureEditingUser = new ConcurrentHashMap<>();

    /**
     * 图片常量
     */
    private static final  String PICTURE_ID = "pictureId";

    /**
     * 建立连接后，给所有用户广播消息
     *
     * @param session 会话
     * @throws Exception 异常
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 将会话保存到集合中
        Long pictureId = (Long) session.getAttributes().get(PICTURE_ID);
        User user = (User) session.getAttributes().get("user");
        pictureSession.putIfAbsent(pictureId, ConcurrentHashMap.newKeySet());
        pictureSession.get(pictureId).add(session);
        // 构造响应，发送用户加入编辑消息
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        String message = String.format("用户 %s 加入编辑", user.getUserName());
        pictureEditResponseMessage.setMessage(message);
        pictureEditResponseMessage.setUser(userService.getUserVO(user));
    }

    /**
     * 握手后发送消息
     *
     * @param session 会话
     * @param message 消息
     * @throws Exception 异常
     */
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 获取信息，将信息（json）转换为 PictureEditRequestMessage
        PictureEditRequestMessage pictureEditRequestMessage = JSONUtil.toBean(message.getPayload(),
                PictureEditRequestMessage.class);
        // 从session属性中获取公共参数
        Map<String, Object> attributes = session.getAttributes();
        User user = (User) attributes.get("user");
        Long pictureId = (Long) attributes.get(PICTURE_ID);
        // 根据类型进行不同的处理
        pictureEditEventProducer.publicEvent(pictureEditRequestMessage, session, user, pictureId);
    }


    /**
     * 关闭连接，移除用户，并通知其他用户
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        Long pictureId = (Long) attributes.get(PICTURE_ID);
        User user = (User) attributes.get("user");
        // 退出编辑状态
        handleExitEdit(null, session, user, pictureId);

        // 移除用户
        Set<WebSocketSession> webSocketSessions = pictureSession.get(pictureId);
        if (webSocketSessions != null) {
            webSocketSessions.remove(session);
            if (webSocketSessions.isEmpty()) {
                pictureSession.remove(pictureId);
            }
        }
        // 响应
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        String message = String.format("%s离开编辑", user.getUserName());
        pictureEditResponseMessage.setMessage(message);
        pictureEditResponseMessage.setUser(userService.getUserVO(user));
        broadcastToPicture(pictureId, pictureEditResponseMessage);
    }


    /**
     * 处理进入编辑操作
     *
     * @param pictureEditRequestMessage 请求消息
     * @param session                   websocket session
     * @param user                      用户
     * @param pictureId                 图片id
     */
    public void handleEnterEdit(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session,
                                User user, Long pictureId) throws IOException {
        // 没有用户正在编辑该图片，才能进入编辑
        if (!pictureEditingUser.containsKey(pictureId)) {
            pictureEditingUser.put(pictureId, user.getId());
            // 构造响应，发送用户加入编辑消息
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ENTER_EDIT.getValue());
            String message = String.format(" %s 正在编辑图片", user.getUserName());
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            broadcastToPicture(pictureId, pictureEditResponseMessage);
        }
    }

    /**
     * 处理编辑操作
     *
     * @param pictureEditRequestMessage 请求消息
     * @param session                   websocket session
     * @param user                      用户
     * @param pictureId                 图片id
     */
    public void handleEnterAction(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session,
                                  User user, Long pictureId) throws IOException {
        Long editingUserId = pictureEditingUser.get(pictureId);
        // 获取编辑行为
        String editAction = pictureEditRequestMessage.getEditAction();
        PictureEditActionEnum actionEnum = PictureEditActionEnum.getEnumByValue(editAction);
        // 校验操作行为是否合法
        if (actionEnum == null) {
            log.error("未知编辑操作: {}", editAction);
            return;
        }
        // 校验当前用户是否是编辑者
        if (editingUserId != null && editingUserId.equals(user.getId())) {
            // 构造响应，发送用户加入编辑消息
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EDIT_ACTION.getValue());
            String message = String.format(" %s 对图片进行 %s", user.getUserName(), actionEnum.getText());
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            broadcastToPicture(pictureId, pictureEditResponseMessage);
        }
    }

    /**
     * 处理退出编辑状态
     *
     * @param pictureEditRequestMessage 请求消息
     * @param session                   websocket session
     * @param user                      用户
     * @param pictureId                 图片id
     */
    public void handleExitEdit(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session,
                               User user, Long pictureId) throws IOException {
        Long editingUserId = pictureEditingUser.get(pictureId);
        if (editingUserId != null && editingUserId.equals(user.getId())) {
            pictureEditingUser.remove(pictureId);
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EXIT_EDIT.getValue());
            String message = String.format("%s退出编辑状态", user.getUserName());
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            broadcastToPicture(pictureId, pictureEditResponseMessage);
        }

    }


    /**
     * 广播图片给所有用户
     *
     * @param pictureId                  图片id
     * @param pictureEditResponseMessage 响应消息
     * @param excludeSession             排除的会话
     */
    private void broadcastToPicture(Long pictureId,
                                    PictureEditResponseMessage pictureEditResponseMessage,
                                    WebSocketSession excludeSession) throws IOException {
        Set<WebSocketSession> webSocketSessions = pictureSession.get(pictureId);
        if (CollectionUtils.isNotEmpty(webSocketSessions)) {
            // 创建 ObjectMapper
            ObjectMapper objectMapper = new ObjectMapper();
            // 配置序列化：将 Long 类型转为 String，解决丢失精度问题
            SimpleModule module = new SimpleModule();
            module.addSerializer(Long.class, ToStringSerializer.instance);
            module.addSerializer(Long.TYPE, ToStringSerializer.instance); // 支持 long 基本类型
            objectMapper.registerModule(module);
            // 序列化为 JSON 字符串
            String message = objectMapper.writeValueAsString(pictureEditResponseMessage);
            TextMessage textMessage = new TextMessage(message);
            for (WebSocketSession webSocketSession : webSocketSessions) {
                if (webSocketSession != null && webSocketSession.equals(excludeSession)) {
                    continue;
                }
                if (webSocketSession.isOpen()) {
                    webSocketSession.sendMessage(textMessage);
                }
            }
        }
    }

    /**
     * 广播图片给所有用户
     *
     * @param pictureId                  图片id
     * @param pictureEditResponseMessage 响应消息
     */
    private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage) throws IOException {
        broadcastToPicture(pictureId, pictureEditResponseMessage, null);
    }
}
